package com.eedi.framework.oauth2.service;

import com.google.common.annotations.VisibleForTesting;
import com.eedi.framework.common.enums.CommonStatusEnum;
import com.eedi.framework.common.enums.UserTypeEnum;
import com.eedi.framework.common.exception.util.ServiceExceptionUtil;
import com.eedi.framework.common.pojo.PageResult;
import com.eedi.framework.common.util.monitor.TracerUtils;
import com.eedi.framework.common.util.servlet.ServletUtils;
import com.eedi.framework.common.util.validation.ValidationUtils;
import com.eedi.framework.logger.controller.sys.vo.loginlog.SysLoginLogCreateReq;
import com.eedi.framework.logger.enmus.SysLoginLogTypeEnum;
import com.eedi.framework.logger.enmus.SysLoginResultEnum;
import com.eedi.framework.logger.service.SysLoginLogService;
import com.eedi.framework.oauth2.controller.org.vo.auth.OrgAuthLoginReq;
import com.eedi.framework.oauth2.controller.org.vo.auth.OrgAuthLoginResp;
import com.eedi.framework.oauth2.convert.OrgAuthConvert;
import com.eedi.framework.oauth2.dal.dataobject.OrgOAuth2AccessTokenDO;
import com.eedi.framework.oauth2.enums.OrgOauth2ErrorCodeConstants;
import com.eedi.framework.permission.service.OrgRoleService;
import com.eedi.framework.user.controller.org.vo.OrgUserAccountPageReq;
import com.eedi.framework.user.controller.org.vo.OrgUserLoginInfoResp;
import com.eedi.framework.user.service.OrgUserService;
import com.xingyuv.captcha.model.common.ResponseModel;
import com.xingyuv.captcha.model.vo.CaptchaVO;
import com.xingyuv.captcha.service.CaptchaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.List;
import java.util.Objects;

import static com.eedi.framework.common.util.servlet.ServletUtils.getClientIP;

/**
 * Auth Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class OrgAuthServiceImpl implements OrgAuthService {

    @Resource
    private OrgUserService orgUserService;
    @Resource
    private SysLoginLogService loginLogService;
    @Resource
    private OrgOAuth2TokenService orgOAuth2TokenService;

    @Resource
    private Validator validator;
    @Resource
    private CaptchaService captchaService;
    @Resource
    private OrgRoleService orgRoleService;

    /**
     * 验证码的开关，默认为 true
     */
    @Value("${eedi.captcha.enable:true}")
    private Boolean captchaEnable;

    @Override
    public OrgUserLoginInfoResp authenticate(String orgUserLoginId, String password) {
        final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME;
        // 校验账号是否存在
        OrgUserLoginInfoResp user = orgUserService.getLoginInfoByLoginId(orgUserLoginId);
        if (user == null) {
            createLoginLog(null, orgUserLoginId, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
            throw ServiceExceptionUtil.exception(OrgOauth2ErrorCodeConstants.ORG_AUTH_LOGIN_BAD_CREDENTIALS);
        }
        if (!orgUserService.isPasswordMatch(orgUserLoginId, password)) {
            createLoginLog(user.getOrgUserId(), orgUserLoginId, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
            throw ServiceExceptionUtil.exception(OrgOauth2ErrorCodeConstants.ORG_AUTH_LOGIN_BAD_CREDENTIALS);
        }
        // 校验是否禁用
        if (CommonStatusEnum.DISABLE.equals(user.getOrgUserAccountStatus())) {
            createLoginLog(user.getOrgUserId(), orgUserLoginId, logTypeEnum, SysLoginResultEnum.USER_DISABLED);
            throw ServiceExceptionUtil.exception(OrgOauth2ErrorCodeConstants.ORG_AUTH_LOGIN_USER_DISABLED);
        }
        return user;
    }

    @Override
    public OrgAuthLoginResp login(OrgAuthLoginReq reqVO) {
        // 校验验证码
        validateCaptcha(reqVO);

        // 使用账号密码，进行登录
        OrgUserLoginInfoResp user = authenticate(reqVO.getOrgUserLoginId(), reqVO.getPassword());

        // 创建 Token 令牌，记录登录日志
        return createTokenAfterLoginSuccess(user.getOrgUserId(), reqVO.getOrgUserLoginId(),getUserType(), SysLoginLogTypeEnum.LOGIN_USERNAME, reqVO.getOrgClientId());
    }

    private void createLoginLog(String userId, String orgUserLoginId,
                                SysLoginLogTypeEnum logTypeEnum, SysLoginResultEnum loginResult) {
        // 插入登录日志
        SysLoginLogCreateReq reqDTO = new SysLoginLogCreateReq();
        reqDTO.setSysLogType(logTypeEnum);
        reqDTO.setSysTraceId(TracerUtils.getTraceId());
        reqDTO.setUserId(userId);
        reqDTO.setUserType(getUserType());
        reqDTO.setSysUserLoginId(orgUserLoginId);
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
        reqDTO.setUserIp(getClientIP());
        reqDTO.setResult(loginResult);
        loginLogService.createLoginLog(reqDTO);
        // 更新最后登录时间
        if (userId != null && Objects.equals(SysLoginResultEnum.SUCCESS, loginResult)) {
            orgUserService.updateUserLogin(userId, getClientIP());
        }
    }

    @VisibleForTesting
    void validateCaptcha(OrgAuthLoginReq reqVO) {
        // 如果验证码关闭，则不进行校验
        if (!captchaEnable) {
            return;
        }
        // 校验验证码
        ValidationUtils.validate(validator, reqVO, OrgAuthLoginReq.CodeEnableGroup.class);
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
        ResponseModel response = captchaService.verification(captchaVO);
        // 验证不通过
        if (!response.isSuccess()) {
            // 创建登录失败日志（验证码不正确)
            createLoginLog(null, reqVO.getOrgUserLoginId(), SysLoginLogTypeEnum.LOGIN_USERNAME, SysLoginResultEnum.CAPTCHA_CODE_ERROR);
            throw ServiceExceptionUtil.exception(OrgOauth2ErrorCodeConstants.ORG_AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg());
        }
    }
    @Override
    public OrgAuthLoginResp createTokenAfterLoginSuccess(String userId, String userLoginId, UserTypeEnum userType, SysLoginLogTypeEnum logType, String orgClientId) {
        // 插入登陆日志
        createLoginLog(userId, userLoginId, logType, SysLoginResultEnum.SUCCESS);
        // 创建访问令牌
        OrgOAuth2AccessTokenDO accessTokenDO = orgOAuth2TokenService.createAccessToken(userId, userType, orgClientId, null);
        // 构建返回结果
        return OrgAuthConvert.INSTANCE.convert(accessTokenDO);
    }

    @Override
    public OrgAuthLoginResp refreshToken(String refreshToken, String clientId) {
        OrgOAuth2AccessTokenDO accessTokenDO = orgOAuth2TokenService.refreshAccessToken(refreshToken, clientId);
        return OrgAuthConvert.INSTANCE.convert(accessTokenDO);
    }

    @Override
    public PageResult<OrgUserLoginInfoResp> query(OrgUserAccountPageReq req) {
        List<String> sysUserIds = orgRoleService.getOrgUserIdByDefaultOrgRoleCode();
        return orgUserService.page(req, sysUserIds);
    }

    @Override
    public void logout(String token, SysLoginLogTypeEnum logType) {
        // 删除访问令牌
        OrgOAuth2AccessTokenDO accessTokenDO = orgOAuth2TokenService.removeAccessToken(token);
        if (accessTokenDO == null) {
            return;
        }
        // 删除成功，则记录登出日志
        createLogoutLog(accessTokenDO.getOrgUserId(), accessTokenDO.getOrgUserType(), logType);
    }

    private void createLogoutLog(String orgUserId, UserTypeEnum userType, SysLoginLogTypeEnum logType) {
        SysLoginLogCreateReq reqDTO = new SysLoginLogCreateReq();
        reqDTO.setSysLogType(logType);
        reqDTO.setSysTraceId(TracerUtils.getTraceId());
        reqDTO.setUserId(orgUserId);
        reqDTO.setUserType(userType);
        reqDTO.setSysUserLoginId(getUsername(orgUserId));
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
        reqDTO.setUserIp(getClientIP());
        reqDTO.setResult(SysLoginResultEnum.SUCCESS);
        loginLogService.createLoginLog(reqDTO);
    }

    private String getUsername(String userId) {
        if (userId == null) {
            return null;
        }
        OrgUserLoginInfoResp orgUserLoginInfoResp = orgUserService.selectByOrgUserId(userId);
        return orgUserLoginInfoResp != null ? orgUserLoginInfoResp.getOrgUserLoginId() : null;
    }

    private UserTypeEnum getUserType() {
        return UserTypeEnum.MEMBER_OPS;
    }

}
